package com.bs.redis.controller;

import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author bingshao
 * @date 2023/3/14
 **/
@RestController
@RequestMapping("/bitmap")
public class BitMapController {

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @PostMapping("/setBit")
    public void setBit() {
        redisTemplate.opsForValue().setBit("20230314", 0, true);
        redisTemplate.opsForValue().setBit("20230314", 3, true);
        redisTemplate.opsForValue().setBit("20230314", 9, true);
        redisTemplate.opsForValue().setBit("20230314", 16, true);
        redisTemplate.opsForValue().setBit("20230314", 20, true);
    }

    @PostMapping("/setBitByte")
    public void setBitByte() {
        redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.setBit("20230315".getBytes(), 0, true));
        redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.setBit("20230315".getBytes(), 9, true));
        redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.setBit("20230315".getBytes(), 10, true));
        redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.setBit("20230315".getBytes(), 11, true));
        redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.setBit("20230315".getBytes(), 12, true));
    }

    @GetMapping("/getBit")
    public void getBit() {
        System.out.println(redisTemplate.opsForValue().getBit("20230314", 3)); //true
        System.out.println(redisTemplate.opsForValue().getBit("20230314", 4)); //false
    }

    @GetMapping("/bitCount")
    public void bitCount() {
        Long count1 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314".getBytes()));
        System.out.println(count1); //5
        Long count2 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230315".getBytes()));
        System.out.println(count2); //5
        // 测试发现bitCount包含start和end两个边界，且注意这默认是byte不是bit
        Long count3 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314".getBytes(), 0, 1));
        System.out.println(count3); //3
        Long count4 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314".getBytes(), 0, 2));
        System.out.println(count4); //5
        Long count5 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314".getBytes(), 0, 3));
        System.out.println(count5); //5
        Long count6 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314".getBytes(), 0, 0));
        System.out.println(count6); //2
    }

    @GetMapping("/bitOp")
    public void bitOp() {
        byte[] bytes1 = "20230314".getBytes();
        byte[] bytes2 = "20230315".getBytes();

        // 交集
        Long count1 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.AND, "20230314_and_15".getBytes(), bytes1, bytes2));
        System.out.println(count1); //3
        Long count5 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314_and_15".getBytes()));
        System.out.println(count5); //2

        // 并集
        Long count2 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.OR, "20230314_or_15".getBytes(), bytes1, bytes2));
        System.out.println(count2); //3  需要的字节数
        Long count6 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314_or_15".getBytes()));
        System.out.println(count6); //8

        // 非
        Long count3 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.NOT, "20230314_not".getBytes(), bytes1));
        System.out.println(count3); //3
        Long count7 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314_not".getBytes()));
        System.out.println(count7); //19

        Long count9 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.NOT, "20230315_not".getBytes(), bytes2));
        System.out.println(count9); //2
        Long count10 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230315_not".getBytes()));
        System.out.println(count10); //11

        // 异或
        Long count4 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitOp(RedisStringCommands.BitOperation.XOR, "20230314_xor_15".getBytes(), bytes1, bytes2));
        System.out.println(count4); //3
        Long count8 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount("20230314_xor_15".getBytes()));
        System.out.println(count8); //6
    }

    @GetMapping("/bitPos")
    public void bitPos() {
        Long index1 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitPos("20230314".getBytes(), true));
        System.out.println(index1); //0  第一个为true（1）的bit位

        Long index2 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitPos("20230314".getBytes(), true, Range.closed(1L, 2L))); // 双闭区间。默认为字节,字节也是从0开始算,
        System.out.println(index2); //9 字节区间[1,2]指的bit位即为[8,23]

        Long index3 = redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitPos("20230314".getBytes(), false));
        System.out.println(index3); //1
    }
}
